# uncompyle6 version 3.2.3
# Python bytecode 3.6 (3379)
# Decompiled from: Python 3.6.8 |Anaconda custom (64-bit)| (default, Feb 21 2019, 18:30:04) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: api\handlers\frontend\issue_trackers.py
import logging, uuid, datetime, requests
from dateutil.tz import tzlocal
from sqlalchemy import select as sql_select
from sqlalchemy import func as sql_func
from api.classes.frontend.auto import *
from api.handlers.frontend import FrontendResourceHandler
from helpers.issue_tracker.clients.client import get_client as issue_tracker_get_client
import helpers.issue_tracker.exceptions as issue_tracker_exceptions
from helpers.licensing.features import Features
from db.data.users import (
    require_master_user,
    get_owner_id,
    require_at_least_tech_admin,
    is_master_user,
)
from db import Connection
from server.http_responses.http_status_codes import *
from server.http_responses.http_errors import Http404, Http500, Http409
from db.tables.issue_trackers import IssueTrackerRow, IssueTrackersTable
from db.tables.targets_configurations import (
    TargetConfigurationRow,
    TargetConfigurationTable,
)
from db.tables.targets import TargetRow, TargetsTable
from settings_provider import settings
from helpers.address import extract_domain_from_address
from helpers.address.firewall import Firewall, FirewallException
from server.http_responses.http_errors import TooManyRequests
from helpers.setup_types import is_setup_type_aws

logger = logging.getLogger("api.Intents")


class IssueTrackers(FrontendResourceHandler):
    def get_issue_trackers(self, **_):
        require_at_least_tech_admin(self.request_user)
        self.license.require(Features.BUG_TRACKING_INTEGRATION)
        q = (
            sql_select((IssueTrackerRow,))
            .where(IssueTrackerRow.owner_id == get_owner_id(self.request_user))
            .where(IssueTrackerRow.deleted_at.is_(None))
        )
        with Connection(self.current_shard_name) as (db_connection):
            issue_trackers = []
            for issue_tracker in db_connection.execute(q).fetchall():
                issue_tracker_data = dict(
                    url=issue_tracker.data["url"],
                    project=issue_tracker.data["project"],
                    bug_tracker=issue_tracker.data["bug_tracker"],
                    issue_tracker_id=issue_tracker.issue_tracker_id,
                    name=issue_tracker.name,
                    issue_type=issue_tracker.data.get("issue_type"),
                )
                if is_master_user(self.request_user):
                    issue_tracker_data["auth"] = issue_tracker.data["auth"]
                issue_trackers.append(ClsIssueTrackerEntry(**issue_tracker_data))

            return ClsIssueTrackerList(issue_trackers=issue_trackers)

    def create_issue_tracker_entry(self, body, **_):
        require_master_user(self.request_user)
        self.license.require(Features.BUG_TRACKING_INTEGRATION)
        issue_tracker = ClsIssueTrackerEntry(**body)
        firewall = settings.get("target_firewall")
        if firewall:
            domain = extract_domain_from_address(issue_tracker.url)
            try:
                firewall.check_address(domain, do_reverse_dns=False)
            except FirewallException as e:
                print(e)
                raise Http409("address not allowed")

            self.check_projects(issue_tracker.to_dict())
            gq = (
                sql_select((sql_func.count(IssueTrackerRow.issue_tracker_id),))
                .where(IssueTrackerRow.owner_id == get_owner_id(self.request_user))
                .where(IssueTrackerRow.deleted_at.is_(None))
            )
            if self.request_user["owner_id"] is None:
                owner_id = self.request_user["user_id"]
                creator_id = self.request_user["user_id"]
            else:
                owner_id = self.request_user["owner_id"]
                creator_id = self.request_user["user_id"]
            iq = (
                IssueTrackersTable.insert().values(
                    issue_tracker_id=str(uuid.uuid4()),
                    owner_id=owner_id,
                    creator_id=creator_id,
                    name=issue_tracker.name,
                    data={
                        k: v
                        for k, v in issue_tracker.to_dict().items()
                        if k not in ("name", "issue_tracker_id")
                    },
                )
            ).returning(IssueTrackerRow.issue_tracker_id)
            with Connection(self.current_shard_name) as (db_connection):
                if is_setup_type_aws():
                    max_issue_tracker_entries = self.license.max_targets + 10
                    issue_tracker_count = db_connection.execute(gq).scalar()
                    if issue_tracker_count + 1 > max_issue_tracker_entries:
                        raise Http409(
                            "The maximum number of issue tracker entries is %s",
                            max_issue_tracker_entries,
                        )
                issue_tracker_id = db_connection.execute(iq).scalar()
                return Http201(
                    location=self.base_path + "/issue_trackers/" + issue_tracker_id
                )

    def delete_issue_tracker_entry(self, issue_tracker_id, **_):
        require_master_user(self.request_user)
        self.license.require(Features.BUG_TRACKING_INTEGRATION)
        q = (
            sql_select((IssueTrackerRow,))
            .where(IssueTrackerRow.issue_tracker_id == issue_tracker_id)
            .where(IssueTrackerRow.owner_id == get_owner_id(self.request_user))
            .where(IssueTrackerRow.deleted_at.is_(None))
        )
        sf = TargetConfigurationTable.join(
            TargetsTable, TargetRow.target_id == TargetConfigurationRow.target_id
        )
        qu = (
            sql_select((TargetConfigurationRow.target_id,))
            .select_from(sf)
            .where(TargetConfigurationRow.name == "issue_tracker_id")
            .where(TargetConfigurationRow.value.has_key(issue_tracker_id))
            .where(TargetRow.deleted_at.is_(None))
        )
        with Connection(self.current_shard_name) as (db_connection):
            original_issue_tracker = db_connection.execute(q).fetchone()
            if not original_issue_tracker:
                raise Http404()
            target_id_list = []
            for e in db_connection.execute(qu).fetchall():
                target_id_list.append(e.target_id)

            if target_id_list:
                raise Http409("Issue tracker is used", details=target_id_list)
            qd = (
                IssueTrackersTable.update(
                    values=dict(deleted_at=datetime.datetime.now(tzlocal()))
                )
            ).where(IssueTrackerRow.issue_tracker_id == issue_tracker_id)
            db_connection.execute(qd)
            return Http204()

    def update_issue_tracker_entry(self, issue_tracker_id, body, **_):
        require_master_user(self.request_user)
        self.license.require(Features.BUG_TRACKING_INTEGRATION)
        issue_tracker = ClsIssueTrackerEntry(
            ignored_required_keys=True, ignore_default_values=True, **body
        )
        firewall = settings.get("target_firewall")
        if firewall:
            domain = extract_domain_from_address(issue_tracker.url)
            try:
                firewall.check_address(domain, do_reverse_dns=False)
            except FirewallException as e:
                print(e)
                raise Http409("address not allowed")

            q = (
                sql_select((IssueTrackerRow,))
                .where(IssueTrackerRow.issue_tracker_id == issue_tracker_id)
                .where(IssueTrackerRow.owner_id == get_owner_id(self.request_user))
                .where(IssueTrackerRow.deleted_at.is_(None))
            )
            modifications = {}
            with Connection(self.current_shard_name) as (db_connection):
                original_issue_tracker = db_connection.execute(q).fetchone()
                if not original_issue_tracker:
                    raise Http404()
                original_issue_tracker_data = original_issue_tracker.data
                if issue_tracker.get("name"):
                    if issue_tracker.name != original_issue_tracker.name:
                        modifications["name"] = issue_tracker.name
                if issue_tracker.get("url"):
                    if issue_tracker.url != original_issue_tracker_data.get("url"):
                        original_issue_tracker_data["url"] = issue_tracker.url
                        modifications["data"] = original_issue_tracker_data
                if issue_tracker.get("project"):
                    if issue_tracker.project.to_dict() != original_issue_tracker_data.get(
                        "project"
                    ):
                        original_issue_tracker_data[
                            "project"
                        ] = issue_tracker.project.to_dict()
                        modifications["data"] = original_issue_tracker_data
                if issue_tracker.get("issue_type"):
                    if issue_tracker.issue_type != original_issue_tracker_data.get(
                        "issue_type"
                    ):
                        original_issue_tracker_data[
                            "issue_type"
                        ] = issue_tracker.issue_type
                        modifications["data"] = original_issue_tracker_data
                if issue_tracker.get("bug_tracker"):
                    if issue_tracker.bug_tracker != original_issue_tracker_data.get(
                        "bug_tracker"
                    ):
                        original_issue_tracker_data[
                            "bug_tracker"
                        ] = issue_tracker.bug_tracker
                        modifications["data"] = original_issue_tracker_data
                if issue_tracker.get("auth"):
                    if issue_tracker.auth.to_dict() != original_issue_tracker_data.get(
                        "auth"
                    ):
                        original_issue_tracker_data[
                            "auth"
                        ] = issue_tracker.auth.to_dict()
                        modifications["data"] = original_issue_tracker_data
            if not modifications:
                return Http204()
            logger.debug("modifications: %s", modifications)
            if modifications.get("data"):
                self.check_projects(modifications["data"])
            q = (IssueTrackersTable.update(values=modifications)).where(
                IssueTrackerRow.issue_tracker_id == issue_tracker_id
            )
            with Connection(self.current_shard_name) as (db_connection):
                db_connection.execute(q)
            return Http204()

    def check_issue_tracker_entry(self, issue_tracker_id, **_):
        require_at_least_tech_admin(self.request_user)
        self.license.require(Features.BUG_TRACKING_INTEGRATION)
        q = (
            sql_select((IssueTrackerRow,))
            .where(IssueTrackerRow.issue_tracker_id == issue_tracker_id)
            .where(IssueTrackerRow.owner_id == get_owner_id(self.request_user))
            .where(IssueTrackerRow.deleted_at.is_(None))
        )
        with Connection(self.current_shard_name) as (db_connection):
            issue_tracker = db_connection.execute(q).fetchone()
            if not issue_tracker:
                raise Http404()
            projects = self.check_projects(issue_tracker.data)
            if issue_tracker.data["project"] not in projects.projects:
                logger.debug(
                    "project not found",
                    issue_tracker.data["project"],
                    projects.projects,
                )
                raise Http500("Issue tracker project not found")
            return ClsIssueTrackerEntry(
                auth=issue_tracker.data["auth"],
                url=issue_tracker.data["url"],
                project=issue_tracker.data["project"],
                bug_tracker=issue_tracker.data["bug_tracker"],
                issue_tracker_id=issue_tracker_id,
                name=issue_tracker.name,
                issue_type=issue_tracker.data.get("issue_type"),
            )

    def check_connection(self, body, **_):
        require_at_least_tech_admin(self.request_user)
        self.license.require(Features.BUG_TRACKING_INTEGRATION)
        issue_tracker = ClsIssueTrackerConfig(**body)
        firewall = settings.get("target_firewall")
        if firewall:
            domain = extract_domain_from_address(issue_tracker.url)
            try:
                firewall.check_address(domain, do_reverse_dns=False)
            except FirewallException:
                raise Http409("address not allowed")

            rate_limiter = settings.get("rt_issue_trackers")
            if rate_limiter:
                t = rate_limiter.wait_time(self.request_user["email"])
                if t:
                    raise TooManyRequests(t)
                rate_limiter.add(self.request_user["email"])
            try:
                issue_tracker_get_client(
                    issue_tracker.to_dict(),
                    settings.get("system_proxy_cb", None),
                    settings.get("ssl_ca_bundle_path", True),
                )
            except issue_tracker_exceptions.ConfigError as e:
                result = ClsIssueTrackerConnectionStatus(success=False, message=str(e))
            except ConnectionError:
                result = ClsIssueTrackerConnectionStatus(
                    success=False, message="Connection error"
                )
            except (requests.Timeout, requests.ReadTimeout):
                result = ClsIssueTrackerConnectionStatus(
                    success=False, message="Connection timeout"
                )
            except requests.URLRequired:
                result = ClsIssueTrackerConnectionStatus(
                    success=False, message="Invalid url"
                )
            except requests.RequestException:
                result = ClsIssueTrackerConnectionStatus(
                    success=False, message="Request exception"
                )
            except Exception as e:
                logger.error(e)
                result = ClsIssueTrackerConnectionStatus(success=False, message="Error")
            else:
                result = ClsIssueTrackerConnectionStatus(success=True, message=None)

            return result

    def check_projects(self, body, **_):
        require_at_least_tech_admin(self.request_user)
        self.license.require(Features.BUG_TRACKING_INTEGRATION)
        issue_tracker = ClsIssueTrackerConfig(**body)
        firewall = settings.get("target_firewall")
        if firewall:
            domain = extract_domain_from_address(issue_tracker.url)
            try:
                firewall.check_address(domain, do_reverse_dns=False)
            except FirewallException as e:
                print(e)
                raise Http409("address not allowed")

            rate_limiter = settings.get("rt_issue_trackers")
            if rate_limiter:
                t = rate_limiter.wait_time(self.request_user["email"])
                if t:
                    raise TooManyRequests(t)
                rate_limiter.add(self.request_user["email"])
        try:
            client = issue_tracker_get_client(
                issue_tracker.to_dict(),
                settings.get("system_proxy_cb", None),
                settings.get("ssl_ca_bundle_path", True),
            )
            projects = client.get_projects()
            return ClsIssueTrackerProjects(projects=projects)
        except issue_tracker_exceptions.ConfigError as e:
            raise Http500(str(e))
        except ConnectionError:
            raise Http500("Connection error")
        except (requests.Timeout, requests.ReadTimeout):
            raise Http500("Connection timeout")
        except requests.URLRequired:
            raise Http500("Invalid url")
        except requests.RequestException:
            raise Http500("Request exception")
        except Exception as e:
            logger.error(e)
            raise Http500("Connection error")

    def check_issue_types(self, body, **_):
        require_at_least_tech_admin(self.request_user)
        self.license.require(Features.BUG_TRACKING_INTEGRATION)
        issue_tracker = ClsIssueTrackerConfig(**body)
        firewall = settings.get("target_firewall")
        if firewall:
            domain = extract_domain_from_address(issue_tracker.url)
            try:
                firewall.check_address(domain, do_reverse_dns=False)
            except FirewallException as e:
                print(e)
                raise Http409("address not allowed")

            project = issue_tracker.get("project")
            if not project:
                raise Http500("No project selected")
            rate_limiter = settings.get("rt_issue_trackers")
            if rate_limiter:
                t = rate_limiter.wait_time(self.request_user["email"])
                if t:
                    raise TooManyRequests(t)
                rate_limiter.add(self.request_user["email"])
        try:
            client = issue_tracker_get_client(
                issue_tracker.to_dict(),
                settings.get("system_proxy_cb", None),
                settings.get("ssl_ca_bundle_path", True),
            )
            issue_types = client.get_issue_types()
            return ClsIssueTrackerIssueTypes(issue_types=issue_types)
        except issue_tracker_exceptions.ConfigError as e:
            raise Http500(str(e))
        except ConnectionError:
            raise Http500("Connection error")
        except (requests.Timeout, requests.ReadTimeout):
            raise Http500("Connection timeout")
        except requests.URLRequired:
            raise Http500("Invalid url")
        except requests.RequestException:
            raise Http500("Request exception")
        except Exception as e:
            logger.error(e)
            raise Http500("Connection error")
